package lsm

import (
	"fmt"
	"learning/gooop/etcd/raft/config"
	"learning/gooop/etcd/raft/logger"
	"learning/gooop/etcd/raft/model"
	"learning/gooop/etcd/raft/rpc"
	"learning/gooop/etcd/raft/rpc/client"
	"learning/gooop/etcd/raft/store"
	"sync"
)

type tRaftLSMImplement struct {
	model.TEventDrivenModel
	mInitOnce sync.Once

	mConfig        config.IRaftConfig
	mStore         store.ILogStore
	mClientService client.IRaftClientService
	mState         IRaftState
}

// trigger: init()
// args: empty
const meInit = "lsm.Init"

// trigger: HandleStateChanged()
// args: IRaftState
const meStateChanged = "lsm.StateChnaged"

func (me *tRaftLSMImplement) init(cfg config.IRaftConfig, logStore store.ILogStore) {
	me.mInitOnce.Do(func() {
		me.mConfig = cfg
		me.mStore = logStore

		me.initEventHandlers()
		me.Raise(meInit)
	})
}

func (me *tRaftLSMImplement) initEventHandlers() {
	// write only
	me.hookEventsForPeerService()
	me.hookEventsForState()
}

func (me *tRaftLSMImplement) hookEventsForPeerService() {
	me.Hook(meInit, func(e string, args ...interface{}) {
		me.mClientService = client.NewRaftClientService(me.mConfig)
	})
}

func (me *tRaftLSMImplement) hookEventsForState() {
	me.Hook(meInit, func(e string, args ...interface{}) {
		me.mState = newFollowerState(me, me.mStore.LastCommittedTerm())
		me.mState.Start()
	})

	me.Hook(meStateChanged, func(e string, args ...interface{}) {
		state := args[0].(IRaftState)
		me.logf(".meStateChanged, %v", state.Role())

		me.mState = state
		state.Start()
	})
}

func (me *tRaftLSMImplement) Config() config.IRaftConfig {
	return me.mConfig
}

func (me *tRaftLSMImplement) Store() store.ILogStore {
	return me.mStore
}

func (me *tRaftLSMImplement) HandleStateChanged(state IRaftState) {
	me.logf(".HandleStateChanged, state=%v", state.Role())
	me.Raise(meStateChanged, state)
}

func (me *tRaftLSMImplement) RaftClientService() client.IRaftClientService {
	return me.mClientService
}

func (me *tRaftLSMImplement) logf(format string, args ...interface{}) {
	prefix := fmt.Sprintf("tRaftLSMImplement(%v,%v)", me.mConfig.GetID(), me.mState.Role())
	logger.Logf(prefix+format, args...)
}

func (me *tRaftLSMImplement) Heartbeat(cmd *rpc.HeartbeatCmd, ret *rpc.HeartbeatRet) error {
	e := me.mState.Heartbeat(cmd, ret)
	me.logf(".Heartbeat, cmd=%v, ret=%v, err=%v", cmd, ret, e)
	return e
}

func (me *tRaftLSMImplement) AppendLog(cmd *rpc.AppendLogCmd, ret *rpc.AppendLogRet) error {
	e := me.mState.AppendLog(cmd, ret)
	me.logf(".AppendLog, cmd=%v, ret=%v, err=%v", cmd, ret, e)
	return e
}

func (me *tRaftLSMImplement) CommitLog(cmd *rpc.CommitLogCmd, ret *rpc.CommitLogRet) error {
	e := me.mState.CommitLog(cmd, ret)
	me.logf(".CommitLog, cmd=%v, ret=%v, err=%v", cmd, ret, e)
	return e
}

func (me *tRaftLSMImplement) RequestVote(cmd *rpc.RequestVoteCmd, ret *rpc.RequestVoteRet) error {
	e := me.mState.RequestVote(cmd, ret)
	me.logf(".RequestVote, cmd=%v, ret=%v, err=%v", cmd, ret, e)
	return e
}

func (me *tRaftLSMImplement) ExecuteKVCmd(cmd *rpc.KVCmd, ret *rpc.KVRet) error {
	e := me.mState.ExecuteKVCmd(cmd, ret)
	me.logf(".ExecuteKVCmd, cmd=%v, ret=%v, err=%v", cmd, ret, e)
	return e
}

func (me *tRaftLSMImplement) State() IRaftState {
	return me.mState
}

func NewRaftLSM(cfg config.IRaftConfig, logStore store.ILogStore) IRaftLSM {
	it := new(tRaftLSMImplement)
	it.init(cfg, logStore)
	return it
}
